/*
*   Segundo Trabalho: SCC-0143
*
*   Palíndromos:
*       Verifica a ocorrência de palíndromos de palavras e frases em um texto;
*       As bibliotecas OpenMP e MPI são utilizadas para a paralelização dos processos.
*/

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>
#include <omp.h>

typedef struct{
    int palavras;
    int frases;
    int tamanhoArq;
}Palindromo;

typedef struct{
    int *operacoes;
    int numThreads;
}Thread;

/*
*   Função Palindromo:
*       Verifica a ocorrência de palindromos com tamanho igual a tamPalavra.
*/
void Pal_OpenMP(char *texto, int tamPalavra, Palindromo *p, Thread t){
    bool palindromo, isword;
    int i, j;
    int str_length;
    char string[tamPalavra], c1a, c1b, c2a, c2b;
    char c, c1, c2;

    /*
    *   Comando for em paralelo com o número de threads igual ao número de núcleos da maquina em execução;
    *   Uma varredura é feita do início do texto até seu fim com o tamanho da palíndromo passado em parâmetro;
    *   Caso um palíndromo seja verificado, é imprimido na tela;
    *   A quantidade de palíndromos encontrados é retornado
    */
    #pragma omp parallel for private(i, j, c1a, c1b, c2a, c2b, c, c1, c2, string, str_length, palindromo, isword)
    for(j=1; j < ((p->tamanhoArq)-tamPalavra); j++){
        palindromo = true;
        isword = true;

        c1b = texto[j];
        c2a = texto[tamPalavra+j-1];

        /*Verifica se o primeiro e o último caracter são dígitos ou letras*/
        if((isalpha(c1b) && isalpha(c2a)) || (isdigit(c1b) && isdigit(c2a))){
            c1a = texto[j-1];
            c2b = texto[tamPalavra+j];

            /*
            *   Verifica se o caracter anterior ao primeiro e o posterior ao último não são dígitos nem letras;
            *   Isso é feito para verificar o ínicio e final de uma palavra ou frase
            */
            if(!isalnum(c1a) && !isalnum(c2b)){
                str_length = 0;

                /*Armazena em string apenas os dígitos e letras*/
                for(i=0; i < tamPalavra; i++){
                    c = texto[j+i];

                    if(isalnum(c)){
                        string[str_length] = c;
                        str_length++;
                    }
                    else{
                        isword = false;
                    }
                }

                /*Verifica se string é um palíndromo*/
                for(i=0; i < (str_length/2); i++){
                    c1 = string[i];
                    c2 = string[str_length-i-1];

                    if(isalpha(c1)){
                        if(isupper(c1))
                            c1 = tolower(c1);
                    }
                    if(isalpha(c2a)){
                        if(isupper(c2a))
                            c2 =  tolower(c2);
                    }

                    if(c1 != c2){
                        palindromo = false;
                        break;
                    }
                }
            }
            else
                palindromo = false;
        }
        else
            palindromo = false;

        /*Caso a palavra ou frase for palíndromo, ela é imprimida no terminal da maneira que está no texto*/
        if(palindromo){
            if(isword)
                p->palavras++;
            else
                p->frases++;

            for(i=0; i < tamPalavra ; i++){
                c = texto[j+i];

                /*Caso o caracter seja retorno de carro é substituído por um espaço*/
                if(c == 13)
                    c = 32;

                printf("%c", c);
            }
            printf("\n");
        }
    }
}

int main(void){
    FILE *pt;
    Palindromo pal;
    Thread th;
    int i;
    char *texto;

    pal.palavras = 0;
    pal.frases = 0;
    th.numThreads = 1;

    th.operacoes = (int*) malloc(th.numThreads*sizeof(int));

    for(i=0;i < th.numThreads; i++)
        th.operacoes[i] = 0;

    /*Abre o arquivo shakespe.txt e armazena em pt para as respectivas manipulações*/
    pt = fopen("shakespe.txt","r");

    if(pt == NULL){
        printf("Nao foi possivel abrir o arquivo!");
        return 0;
    }

    /*Armazena o tamanho do arquivo na variável _tamanho*/
    fseek(pt, 0, SEEK_END);
    pal.tamanhoArq = ftell(pt);

    texto = (char*) malloc(pal.tamanhoArq*sizeof(char));

    /*Armazena o conteúdo do arquivo na string texto*/
    fseek(pt, 0, SEEK_SET);
    fread(texto, 1, pal.tamanhoArq, pt);

    /*
    *   Verifica a ocorrência de palíndromos em palavras e frases no texto com o tamanho de i;
    *   os últimos palíndromos a ocorrerem no texto possuem tamanho 22
    *   São criadas 12 threads para a realizacao da iteração; assim o comando for irá ser dividido em 12 partes, sendo cada parte processada em uma thread
    */
    omp_set_num_threads(th.numThreads);

    #pragma omp parallel for
    for(i=2; i < 22; i++){
        Pal_OpenMP(texto, i, &pal, th);         (th.operacoes[omp_get_thread_num()])++;
    }

    printf("\nPalindromos:\n\nPalavras = %d\nFrases = %d\n\nTotal = %d\n", pal.palavras, pal.frases, pal.palavras+pal.frases);

    for(i=0; i < th.numThreads; i++)
        printf("Thread %d: %d\n", i,th.operacoes[i]);

    return 0;
}
